package com.bana.sys.socket;

import com.bana.sys.code.SendResult;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * tcp客户端模式的网络连接
 */
@ChannelHandler.Sharable
public class ClientChannel extends SocketChannel {

    private static Logger logger= LogManager.getLogger(ServerChannel.class);

    /**
     * netty相关类
     */
    private ChannelHandlerContext context;
    private EventLoopGroup workerGroup;

    /**
     * 集群节点ip
     */
    private String ip;

    /**
     * 集群节点端口
     */
    private int port;

    /**
     * 集群节点是否已经连接上
     */
    private boolean connected;


    /**
     * 构造函数
     * @param ip 集群节点ip
     * @param port 集群节点端口
     */
    public ClientChannel(String ip, int port)
    {
        this.ip=ip;
        this.port=port;
        connected=false;
        context=null;
        workerGroup=null;
    }

    /**
     * 获取当前通道连接tcp服务的地址
     * @return tpc服务地址
     */
    public String getIp()
    {
        return this.ip;
    }

    /**
     * 获取节点是否已经连接上
     * @return true表示已经连接上
     */
    public boolean getConnected()
    {
        return connected;
    }

    /**
     * 连接集群节点
     */
    public void connect()
    {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try
        {
            Bootstrap boot = new Bootstrap();
            boot.group(workerGroup);
            boot.channel(NioSocketChannel.class);
            boot.option(ChannelOption.SO_KEEPALIVE, true);
            boot.handler(this);
            boot.connect(ip, port).sync();
            connected=true;
            this.workerGroup=workerGroup;
            logger.info("connect "+ip+" "+port);
        }
        catch(Exception e)
        {
            workerGroup.shutdownGracefully();
            this.workerGroup=null;
            logger.info(e);
        }
    }

    public SendResult sendTcp(byte[] buffer)
    {
        //发送数据
        return sendTcp(this.context,buffer);
    }

    /**
     * 发送协议
     * @param id 协议编号
     * @param timeStamp 时间戳
     * @param buffer 发送内容
     * @return 发送结果
     */
    public SocketResult sendTcp(int id,long timeStamp, byte[] buffer)
    {
        return sendTcp(id,timeStamp,0,0,0,buffer,3000);
    }
    /**
     * 发送协议
     * @param id 协议编号
     * @param timeStamp 时间戳
     * @param buffer 发送内容
     * @return 发送结果
     */
    public SocketResult sendTcp(int id,long timeStamp, byte[] buffer, int timeout)
    {
        return sendTcp(id,timeStamp,0,0,0,buffer,timeout);
    }

    /**
     * 发送协议
     * @param id 协议编号
     * @param timeStamp 时间戳
     * @param buffer 发送内容
     * @return 发送结果
     */
    public SocketResult sendTcp(int id,long timeStamp,int port,int controllerId,int controllerProtocolId, byte[] buffer)
    {
        return sendTcp(id,timeStamp,port,controllerId,controllerProtocolId,buffer,3000);
    }

    /**
     * 发送协议
     * @param id 协议编号
     * @param timeStamp 时间戳
     * @param buffer 发送内容
     * @param timeout 等待响应超时时间
     * @return 发送结果
     */
    public SocketResult sendTcp( int id,long timeStamp,int port,int controllerId,int controllerProtocolId, byte[] buffer, int timeout)
    {
        //异步回调
        NoticeHandler handler=new NoticeHandler(id,timeStamp,port,controllerId,controllerProtocolId);


        //注册异步等待
        addAsyncHandler(handler);

        //发送数据
        SendResult result= sendTcp(this.context,buffer);
        SocketResult sr=new SocketResult();
        sr.setResult(result);
        if(result==SendResult.Success)
        {
            //等待响应
            if (handler.await(timeout))
            {
                sr.setReceive(handler.getBuffer());
            }
            else
            {
                sr.setResult(SendResult.Timeout);
            }
        }
        return sr;
    }

    /**
     * 关闭连接
     */
    public void close()
    {
        if(context!=null)
        {
            context.close();
        }
        context=null;
        workerGroup.shutdownGracefully();
        connected=false;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){

        receive(ctx,(ByteBuf) msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        context=ctx;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        context=null;
        close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){

        context=null;
        logger.info("exception "+ctx.name()+" "+cause.getMessage());
        close();
    }

}
